# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2022/9/1
# @Author  : moe@miku39.fun
# @File    : __main__.py
# @Software: PyCharm

import argparse
import os
import shutil
import time

import requests

from . import (__banner__, __version__,
               checkVersion)
from bili.service.impl import Video, AnimeFilm
from bili.util import FFmpeg, ParserURL

# 检测版本
checkVersion()
# 工作目录
WORK_PATH = os.getcwd()
# 文件临时输出路径
TEMP_PATH = os.path.join(WORK_PATH, "temp")
# 视频持久化处理存放路径
OUTPUT_PATH = os.path.join(WORK_PATH, "output")


class Bili:
    def __init__(self, config) -> None:
        self.ffmpegEXEC = None
        self.videoSourceList = None
        self.videoInfo = None
        self.config = config
        self.banner = __banner__
        self.system = os.name
        self.sess = requests.session()
        self.parserOfUrl = ParserURL.URLOperation(self.sess)
        self.ffmpeg = FFmpeg.FFmpegOperation
        self.spider = None
        self.sess.headers.update(
            {
                "origin": "https://www.bilibili.com",
                "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                              "Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.67",
            }
        )

    @classmethod
    def init_dir(cls, tmp, out):
        if not os.path.exists(tmp):
            os.makedirs(tmp)
        if not os.path.exists(out):
            os.makedirs(out)

    def login(self) -> None:
        for key, value in self.config["account"].items():
            self.sess.cookies.set(key, value)
        self.warpOption()

    def warpOption(self):
        self.config["option"]["blockSize"] = (
            self.config["option"]["blockSize"] * 1024 * 1024
        )

    def setReferer(self, vide_base_info) -> None:
        if vide_base_info["idType"] == "season_id":
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/bangumi/play/ss{vide_base_info['videoID']}"
                }
            )
        elif vide_base_info["idType"] == "ep_id":
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/bangumi/play/ep{vide_base_info['videoID']}"
                }
            )
        else:
            self.sess.headers.update(
                {
                    "referer": f"https://www.bilibili.com/video/{vide_base_info['videoID']}"
                }
            )

    def getVipStatus(self) -> bool:
        url = "https://api.bilibili.com/x/web-interface/nav"
        response = self.sess.get(url=url).json()
        if response["code"] == -101 and response["data"]["isLogin"] is False:
            return False
        elif response["code"] == 0 and response["data"]["isLogin"] is True:
            if response["data"]["vipStatus"] == 0:  # 未开通大会员
                return False
            elif response["data"]["vipStatus"] == 1:  # 已开通大会员
                return True

    def parserVideo(self) -> bool:
        if videoBaseInfo := self.parserOfUrl.match(self.config["videoBaseData"]["url"]):
            if videoBaseInfo["videoType"] == "video":
                self.setReferer(videoBaseInfo)
                video = Video.Video(self.sess)
            elif videoBaseInfo["videoType"] == "animeFilm":
                if self.getVipStatus() is False:
                    print("根据您提交的视频号，判断该视频是番剧，需提供大会员账号 （￣へ￣）")
                    return False
                self.setReferer(videoBaseInfo)
                video = AnimeFilm.AnimeFilm(self.sess)
            else:
                print("视频不存在 或 该视频仅支持海外环境 (-_-#)")
                return False
        else:
            print("视频地址有误 或 该视频仅支持海外网络 (-_-#)")
            return False
        if videoInfo := video.getVideoInfo(videoBaseInfo):
            self.videoInfo = videoInfo
            if result := video.getVideoPlayUrl2(videoBaseInfo, self.videoInfo, self.config["videoBaseData"]):
                self.videoSourceList = result
                return True
            return False

    def runners(self) -> None:
        Bili.init_dir(TEMP_PATH, OUTPUT_PATH)
        if ffmpegEXEC := self.ffmpeg.existFFmpeg():
            self.ffmpegEXEC = ffmpegEXEC
        else:
            print("检测到未配置FFmpeg (´･_･`)")
            exit(1)
        if self.config["account"] != "":
            self.login()
        if self.parserVideo() is True:
            if self.config["option"]["multiprocess"]:
                from bili.download import MultiprocessSpider

                self.spider = MultiprocessSpider.Spider(self.sess, self.system)
            else:
                from bili.download import AsyncSpider

                self.spider = AsyncSpider.Spider(self.sess, self.system)
            videoBlockList = self.spider.download(self.videoInfo, self.videoSourceList, self.config["option"],
                                                  self.banner)
            for each in videoBlockList:
                self.ffmpeg.merge(self.ffmpegEXEC, each["videoPath"], each["audioPath"], each["FinalPath"])
            time.sleep(1)
            print("\n视频已下载全部完成，快去看看吧 (๑>؂<๑）\n")
        if not self.config["option"]["keepTempFiles"]:
            try:
                shutil.rmtree(TEMP_PATH)
            except Exception as error:
                print(f"删除临时视频文件失败，失败原因: {error}")
        else:
            exit(1)


def wrapDict(inner_args):
    return {
        "option": {
            "outputPath": inner_args.dir,
            "tempPath": TEMP_PATH,
            "keepTempFiles": False,
            "blockSize": inner_args.block_size,
            "stream": inner_args.stream,
            "multiprocess": inner_args.multiprocess,
            "workerNum": inner_args.num,
        },
        "account": {
            "SESSDATA": inner_args.cookie
        },
        "videoBaseData": {
            "url": inner_args.url,
            "episodes": inner_args.episodes,
            "yes": inner_args.yes,
            "videoQuality": inner_args.quality,
            "audioQuality": inner_args.audio_quality,
        },
    }


def args():
    parser = argparse.ArgumentParser(description="b站视频下崽器", prog="bili")
    parser.add_argument("url", help="视频号或视频地址")
    parser.add_argument(
        "-b",
        "--block-size",
        default=128,
        type=int,
        help="分块下载器的块大小，单位为 MiB, 默认为 128MiB, 数值为 0 禁用分块下载",
    )
    parser.add_argument("-c", "--cookie", default="", help="用户Cookies")
    parser.add_argument("-d", "--dir", default=OUTPUT_PATH, help="下载目录")
    parser.add_argument(
        "-m",
        "--multiprocess",
        default=False,
        choices=[True, False],
        type=bool,
        help="是否启用多进程，默认否。 PS:视频下载归类于IO密集型，通常无需使用多进程",
    )
    parser.add_argument(
        "-n", "--num", default=4, type=int, help="同时下载的视频个数, PS:多进程环境下，每个视频将启用一个进程参与下载"
    )
    parser.add_argument(
        "-p", "--episodes", default="", help="选集,视频号(num) 或 视频范围(start-end) 以英文逗号分隔"
    )
    parser.add_argument(
        "-q",
        "--quality",
        default=127,
        choices=[127, 125, 120, 116, 112, 80, 74, 64, 32, 16],
        type=int,
        help="127: '8K 超高清', 125: 'HDR 真彩', 120: '超清 4K', 116: '60帧 1080P', 112: '高码率 1080P', 80: '高清 1080P', "
             "74: '60帧 720P', 64: '高清 720P', 32: '清晰 480P', 16: '流畅 360P'",
    )
    parser.add_argument(
        "-s",
        "--stream",
        default=True,
        choices=[True, False],
        type=bool,
        help="流媒体下载，默认启用，流媒体能够缓解紧张的内存资源",
    )
    parser.add_argument(
        "-v", "--version", action="version", version=f"bili {__version__}"
    )
    parser.add_argument("-y", "--yes", action="store_true", help="任何询问一律同意")
    parser.add_argument(
        "--audio-quality",
        default=30280,
        choices=[30280, 30232, 30216],
        type=int,
        help="30280: '高音质', 30232: '中音质', 30216: '低音质'",
    )
    return parser


def main():
    parser = args()
    dic = wrapDict(parser.parse_args())
    Bili(dic).runners()


if __name__ == "__main__":
    main()
